/*
half pipe lua, written by -

most functions here are to make usage of this script easier
the only ones that has to be attached are the ones to 
*/
local localver = 1
local localname = "TonyHawk"
local localrev = 0


//look for half pipesl ine in a map to know is this library should be used somewhere
local halfpipelinessearched = false
local function LocaStoreHalfPipeLines()
	if halfpipelinessearched == true then return end //yeeeeeeee, netgames and desynchs
	halfpipelinessearched = true
	for line in lines.iterate do
		if not (line and line.valid and line.special == 740) then continue end
		HalfPipe.proceed = true
		break
	end

end 


local function LocalSign(s) //bruh
	if s < 0 then 
		return -1 
	else 
		return 1 
	end
end

local function LocalClamp(val, mi, ma)
	return max(mi, min(ma, val))
end

local function localAngleToInt(angle, addtn) 
	local added = false
	//if not add then add = 0 end
	angle = $ / ANG1
	if not addtn then addtn = 0 end
	
	repeat 
		if angle < 0 then
			angle = $ + 360
		end
		if angle > 360 then
			angle = $ - 360
		end
		if added == false then
			added = true
			angle = $ + addtn
		end
	until (angle >= 0 and angle <= 360)

	return angle
end

//backported from source, gives in which line of al ine isa the point you give
//line can be a real life, or a table pretending to be a line
local function localP_PointOnLineSide(x, y, line)
	local v1 = line.v1
	local dx, dy, left, right

	if not line.dx then
		if x <= v1.x then
			return (line.dy > 0)
		end
	
		return (line.dy < 0)
	end
	if not line.dy then
		if y <= v1.y then
			return (line.dx < 0)
		end

		return (line.dx > 0)
	end

	dx = x - v1.x
	dy = y - v1.y

	left = FixedMul(line.dy / FRACUNIT, dx)
	right = FixedMul(dy, line.dx / FRACUNIT)

	if right < left then
		return 0 // front side
	end
	return 1 // back side

end




//gets the closes point in al ine, but clamped to the line edges
//x and y are the coordinates you wanna test this
//all the other ones can be given OR ommited by a REAL linedef
//or a table (can be unnamed or naed by x1,y1, x2,y3 fields)
local function LocalHalfPipeGetCPOL(x, y, x1, y1, x2, y2)
	local cpol , mi, ma, line = {}, 0, 0, {}
	if type(x1) == "userdata" then
		cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1)
		line = x1
	elseif type(x1) == "table"
		if line[1] == nil then
			cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1.x1, x1.y1, x1.x2, x1.y2 )
			line.v1.x, line.v1.y, line.v2.x, liene.v2.y = x1.x1, x1.y1, x1.x2, x1.y2
		else
			cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1[1], x1[2], x1[3], x1[4] )
			line.v1.x, line.v1.y, line.v2.x, liene.v2.y = x1[1], x1[2], x1[3], x1[4]
		end
		
	else
		cpol.x, cpol.y = P_ClosestPointOnLine(x,y, x1, y1, x2, y2 )
		line.v1.x, line.v1.y, line.v2.x, liene.v2.y = x1, y1, x2, y2 
	end
	
	//now we clamp this point to the line range
	mi = min(line.v1.x, line.v2.x)
	ma = max(line.v1.x, line.v2.x)
	cpol.x = LocalClamp($, mi, ma)
	
	mi = min(line.v1.y, line.v2.y)
	ma = max(line.v1.y, line.v2.y)
	cpol.y = LocalClamp($, mi, ma)
	
	return cpol
end



//gets the closest half pipe line
//type defines how it wanna be searched
//noone or 0 is by distance from the closest point
//1 ir by collision with it 
//radius is necessary if you wanna find lines by collision
//is the distance it should use from the origin to detect collsiion with lines
local function LocalNearestHalfPipeLine(x ,y, searchtype, radius, ignore)
	local foundline = nil
	local closest, result, collist, defaultside = INT32_MIN, nil, {}, -1
		
	if not searchtype then searchtype = 0 end
	if not radius then radius = 42*FRACUNIT end

	local dummy = P_SpawnMobj(x,y,0,MT_GARGOYLE)
	dummy.flags = $ & ~MF_SOLID
	dummy.flags2 = $ | MF2_DONTDRAW
	dummy.flags = $ | (MF_NOCLIP|MF_NOGRAVITY)
    searchBlockmap("lines", function(mo, line)
        if not (line and line.valid
        and line.special == 740) then return nil end
        if not foundline then foundline = {} end
		table.insert(foundline, line)
        
    end, dummy , x - (radius*2), x + (radius*2), y - (radius*2), y + (radius*2))
	P_RemoveMobj(dummy)
	
	//oops
	if not foundline then return nil end
	
	for i = 1, #foundline do
		local line, dist, cpol, shouldignore
		line = foundline[i]
		if ignore then
			if type(ignore) == "userdata" and ignore.valid then //singlel ine
				if ignore.v1 then //check if it is a line
					if ignore == line then
						shouldignore = true
					end
				end
			else
				if type(ignore) == "table" then //table of lines
					for j = 1, #ignore do
						if (ignore[j] and ignore[j].valid )
						and type(ignore[j]) == "userdata"
						and ignore[j].v1
						and ignore[j] == line then
							shouldignore = true
							break
						end
						
						if ignore[j]
						and ((ignore[j]["wall"] and ignore[j]["wall"] == line)
						or (ignore[j]["line"] and ignore[j]["line"] == line)) then
							shouldignore = true
							break 
						end
						
						//table with tables, that may contain lines
						if ignore[j] and type(ignore[j]) == "table" then
							
							for k = 1, #ignore[j] do
								if ignore[j][k]
								and type(ignore[j][k]) == "userdata"
								and ignore[j][k].v1
								and ignore[j][k] == line then
									shouldignore = true
									break 2
								end
								
								
							end
						end
					end
				end
			
			end

		end
		if shouldignore then continue end
		cpol = HalfPipe.funcs.pointonline(x, y, line)
	
		dist = FixedHypot(x - cpol.x, y - cpol.y)
		
		if searchtype == 0 then
			if dist > closest then
				closest = dist
				result = {}
				result.wall = line
				result.angle = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y)
				result.front = HalfPipe.funcs.wallfrontangle(line, false)
				result.back = HalfPipe.funcs.wallfrontangle(line, true)
			end
		end
		
		if searchtype == 1 then
			/*
				brief explanation
				the game does collision with a box (the 4 eges of the player object in a specific distance)
				what i recoomend is t ogive the radius is  the player radius + its speed + (FRACUNIT*12) 
				to MAKE ABSOLUTELY sure it will happen bedofre the game with aly line
				here instead of 4 sides, we'll have 16
				and if ANY side collides it will be added to a list, and the line that has more collisions, wins
			*/
			
			//wanna use this to check which side is not collision
			//because the player never goes throuhg the line itself
			defaultside = HalfPipe.funcs.poitonlineside(x, y, line)
			
			local colcount = 0
			
			for j = 1, 16 do
				local xx, yy, pointcollision
				xx = x + P_ReturnThrustX(nil, (ANGLE_45/2) * j, radius)
				yy = y + P_ReturnThrustY(nil, (ANGLE_45/2) * j, radius)
				pointcollision = HalfPipe.funcs.poitonlineside(xx, yy, line)
				
				//collided (actually is in a different side of the line than the origin)
				//to avoind "collision" with lines farther away, use the dist 
				if pointcollision ~= defaultside
				and dist <= radius + (4*FRACUNIT) then
					colcount = $ + 1
				end
				
			end
			
			//after we count the amount of sides that collided
			//we add this line to the table
			//ONLY IF a collsiion hapenned, ofc
			if colcount ~= 0 then
				table.insert(collist, {line, colcount})
				//sort everytime we found a new one
				
				table.sort(collist, function(a, b) return b[2] > a[2] end)
				
				result = {}
				result.wall = collist[1][1]
				result.angle = R_PointToAngle2(result.wall.v1.x, result.wall.v1.y, result.wall.v2.x, result.wall.v2.y)
				result.front = HalfPipe.funcs.wallfrontangle(result.wall, false)
				result.back = HalfPipe.funcs.wallfrontangle(result.wall, true)
			
			end
			
		end
		
	end
	return result
end

//get the wall front angle
local function LocalWallFrontAngle(line, back)
	local side, lineang, linelenght, pos, dir = nil, nil , nil, {}, ANGLE_90
	lineang = R_PointToAngle2(line.v1.x, line.v1.y, line.v2.x, line.v2.y)
	linelenght = FixedHypot(line.v1.x - line.v2.x, line.v1.y - line.v2.y)
	
	//go to thel ine mdidle
	pos.x = line.v1.x + P_ReturnThrustX(nil, lineang, linelenght/2)
	pos.y = line.v1.y + P_ReturnThrustY(nil, lineang, linelenght/2)
	
	//thrust forwards a bit
	pos.x = $ + P_ReturnThrustX(nil, lineang + ANGLE_90, 16*FRACUNIT)
	pos.y = $ + P_ReturnThrustY(nil, lineang + ANGLE_90, 16*FRACUNIT)
	
	//test to know which side it is
	side = HalfPipe.funcs.poitonlineside(pos.x, pos.y, line)	
	
	if ((side == true or side == 1) and (back == nil or back == false)) //is in backside, invert, if you want the front
	or ((side == false or side == 0) and back == true) then 	//is in the front, invert because you want the back
		dir = -ANGLE_90
	end

	
	return lineang + dir 
end

/*
the folowing was done by Haya, oh boy


*/


-- another edited version of a function that really does not need an onground check :)))))))
local function K_GetKartSpeed(player, doboostpower)
	local k_speed = 150
	local g_cc = FRACUNIT
	local xspd = 3072
	local kartspeed = player.kartspeed
	local finalspeed

	if gamespeed == EASYSPEED then
		g_cc = 53248 + xspd -- 50cc =  81.25 + 4.69 =  85.94%
	elseif gamespeed == HARDSPEED then
		g_cc = 77824 + xspd -- 150cc = 118.75 + 4.69 = 123.44%
	else -- NORMALSPEED
		g_cc = 65536 + xspd -- 100cc = 100.00 + 4.69 = 104.69%
	end

	if G_BattleGametype() and player.kartstuff[k_bumper] <= 0 then
		kartspeed = 1
	end

	k_speed = $ + kartspeed * 3 -- 153 - 177

	finalspeed = FixedMul(FixedMul(k_speed << 14, g_cc), player.mo.scale);

	if doboostpower then
		return FixedMul(finalspeed, player.kartstuff[k_boostpower] + player.kartstuff[k_speedboost])
	end

	return finalspeed
end

local ORIG_FRICTION = 62914

// having to make our own accelcode is painful, you know
local function fuck(player, forwardmove)
	local playerButtons = player.cmd.buttons
	local accelmax = 4000
	local newspeed, oldspeed
	local p_speed = K_GetKartSpeed(player, true)
	local p_accel = K_GetKartAccel(player)
	// ACCELCODE!!!1!11!
	oldspeed = R_PointToDist2(0, 0, player.rmomx, player.rmomy) // FixedMul(P_AproxDistance(player.rmomx, player.rmomy), player.mo.scale)
	newspeed = FixedDiv(FixedDiv(FixedMul(oldspeed, accelmax - p_accel) +
		FixedMul(p_speed, p_accel), accelmax), ORIG_FRICTION)

	local finalSpeed = newspeed - oldspeed

	-- forwardmove is:
	--  50 while accelerating,
	--  25 while clutching,
	--   0 with no gas, and
	-- -25 when only braking.

	local forwardmove 	= 0
	local holdingGas 	= (playerButtons & BT_ACCELERATE) == BT_ACCELERATE
	local holdingBrake 	= (playerButtons & BT_BRAKE) == BT_BRAKE
	if holdingGas and not holdingBrake then -- Accelerating
		forwardmove = 50
	elseif holdingGas and holdingBrake then -- Clutching
		forwardmove = 25
	elseif not holdingGas and holdingBrake then -- Braking
		forwardmove = -25
	end

	finalSpeed = $ * (forwardmove/25)
	finalSpeed = $ / 2

	if forwardmove < 0 and finalSpeed > FRACUNIT*2 then
		finalSpeed = finalSpeed/2
	elseif forwardmove < 0 then
		finalSpeed = -FRACUNIT/2
	end

	if finalSpeed < 0 then finalSpeed = 0 end

	return finalSpeed
end

// aa
local function fuck2(player, cmd)
	//movepushforward = cmd.forwardmove * (thrustfactor * acceleration)
	local totalthrust = {}
	totalthrust.x, totalthrust.y = 0, 0// I forget if this is needed
	local movepushforward = fuck(player, cmd.forwardmove)

	// don't need to account for scale here with kart accel code
	//movepushforward = FixedMul(movepushforward, player.mo.scale)
	if (player.mo.movefactor ~= FRACUNIT) then // Friction-scaled acceleration...
		movepushforward = FixedMul(movepushforward, player.mo.movefactor)
	end

	if (cmd.buttons & BT_BRAKE  and  cmd.forwardmove == 0) then // SRB2kart - braking isn't instant
		movepushforward = $ / 64
	end

	if (cmd.forwardmove > 0) then
		player.kartstuff[k_brakestop] = 0
	elseif (player.kartstuff[k_brakestop] < 6) then // Don't start reversing with brakes until you've made a stop first
		if (player.speed < 8*FRACUNIT) then
			player.kartstuff[k_brakestop] = $ + 1
		end
		movepushforward = 0
	end
	
	return movepushforward
end

// countersteer is how strong the controls are telling us we are turning
// turndir is the direction the controls are telling us to turn, -1 if turning right and 1 if turning left
local function KL_GetKartDriftValue(player, countersteer)
	local basedrift, driftangle;
	local driftweight = player.kartweight*14; // 12

	// If they aren't drifting or on the ground this doesn't apply
	if (player.kartstuff[k_drift] == 0)
		return 0;
	end

	if (player.kartstuff[k_driftend] != 0)
		return -266*player.kartstuff[k_drift]; // Drift has ended and we are tweaking their angle back a bit
	end

	//basedrift = 90*player->kartstuff[k_drift]; // 450
	//basedrift = 93*player->kartstuff[k_drift] - driftweight*3*player->kartstuff[k_drift]/10; // 447 - 303
	basedrift = 83*player.kartstuff[k_drift] - (driftweight - 14)*player.kartstuff[k_drift]/5; // 415 - 303
	driftangle = abs((252 - driftweight)*player.kartstuff[k_drift]/5);
	
	return basedrift + FixedMul(driftangle, countersteer);
end

freeslot("MT_BRAKEDRIFT_JUG", "sfx_jugadr")
-- mobjinfo[MT_BRAKEDRIFT_JUG] = mobjinfo[MT_BRAKEDRIFT]
mobjinfo[MT_BRAKEDRIFT_JUG] =
{ 	
	doomednum = -1,
	spawnstate = S_BRAKEDRIFT,
	spawnhealth = 1000,
	seestate = S_NULL,
	seesound = sfx_None,
	reactiontime = 8,
	attacksound = sfx_None,
	painstate = S_NULL,
	painchance = 0,
	painsound = sfx_None,
	meleestate = S_NULL,
	missilestate = S_NULL,
	deathstate = S_NULL,
	xdeathstate = S_NULL,
	deathsound = sfx_None,
	speed = 8,
	radius = 8*FRACUNIT,
	height = 8*FRACUNIT,
	mass = 100, 
	damage = 0,
	activesound = sfx_None,
	flags = MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_DONTENCOREMAP, 
	raisestate = S_NULL
}

-- lmao of course this function is not exposed
local function K_SpawnDriftSparks(player, pulse)
	if player == nil or not player.valid or
		player.mo == nil or not player.mo.valid then return end
	if leveltime % 2 == 1 then return end

	local pks_drift = player.kartstuff[k_drift]
	local pks_charge = player.kartstuff[k_driftcharge]
	if not pks_drift or pks_charge < K_GetKartDriftSparkValue(player) then return end

	local playerMo		= player.mo
	local playerAngle 	= playerMo.angle
	local playerScale	= playerMo.scale
	local playerSpark	= K_GetKartDriftSparkValue(player)
	local cmdDriftTurn	= player.cmd.driftturn

	local travelangle = playerAngle - ((ANGLE_45 / 5) * pks_drift);

	for i = 0,2 do
		local fakeTerniary = 1;
		if i & 1 then fakeTerniary = -1 end 

		local newx = playerMo.x + P_ReturnThrustX(playerMo, travelangle + fakeTerniary * ANGLE_135, FixedMul(32 * FRACUNIT, playerScale))
		local newy = playerMo.y + P_ReturnThrustY(playerMo, travelangle + fakeTerniary * ANGLE_135, FixedMul(32 * FRACUNIT, playerScale))
		local sparkMobj = P_SpawnMobj(newx, newy, playerMo.z, MT_DRIFTSPARK)

		sparkMobj.target 	= playerMo
		sparkMobj.angle 	= travelangle - ((ANGLE_45 / 5) * pks_drift)
		sparkMobj.destscale = playerScale;
		P_SetScale(sparkMobj, playerScale);

		sparkMobj.momx = playerMo.momx / 2;
		sparkMobj.momy = playerMo.momy / 2;

		if pks_charge >= playerSpark * 4 then
			sparkMobj.color = 1 + (leveltime % (MAXSKINCOLORS - 1))

		elseif pks_charge >= playerSpark * 2 then
			if pks_charge <= (playerSpark * 2) + (24 * 3) then
				sparkMobj.color = SKINCOLOR_RASPBERRY -- transition
			else
				sparkMobj.color = SKINCOLOR_KETCHUP
			end
		else
			sparkMobj.color = SKINCOLOR_SAPPHIRE
		end

		if (pks_drift > 0 and cmdDriftTurn > 0) or -- Inward drifts
			(pks_drift < 0 and cmdDriftTurn < 0) then

			if (pks_drift < 0 and (i & 1)) or (pks_drift > 0 and not (i & 1)) then
				sparkMobj.state = S_DRIFTSPARK_A1
			elseif (pks_drift < 0 and not (i & 1)) or (pks_drift > 0 and (i & 1)) then
				sparkMobj.state =  S_DRIFTSPARK_C1
			end

		elseif (pks_drift > 0 and cmdDriftTurn < 0) or -- Outward drifts
				(pks_drift < 0 and cmdDriftTurn > 0) then

			if (pks_drift < 0 and (i & 1)) or (pks_drift > 0 and not (i & 1)) then
				sparkMobj.state = S_DRIFTSPARK_C1
			elseif (pks_drift < 0 and not (i & 1)) or (pks_drift > 0 and (i & 1)) then
				sparkMobj.state = S_DRIFTSPARK_A1
			end

		end

		K_MatchGenericExtraFlags(sparkMobj, playerMo)
	end
end

local function K_SpawnBrakeDriftSparks(player)
	if player == nil or not player.valid or
		player.mo == nil or not player.mo.valid then return end

	local playerMo = player.mo

	-- Position & etc are handled in its thinker, and its spawned invisible.
	-- This avoids needing to dupe code if we don't need it.
	local mobjSparks = P_SpawnMobj(playerMo.x, playerMo.y, playerMo.z, MT_BRAKEDRIFT_JUG)
	mobjSparks.target = playerMo

	mobjSparks.destscale = playerMo.scale
	P_SetScale(mobjSparks, mobjSparks.destscale)
	K_MatchGenericExtraFlags(mobjSparks, playerMo)

	mobjSparks.flags2 = $ | MF2_DONTDRAW;
end

local function K_SpawnAIZDust(player)
	if player == nil or not player.valid or
		player.mo == nil or not player.mo.valid then return end
	if leveltime % 2 == 1 then return end

	local playerMo = player.mo
	local travelangle = R_PointToAngle2(0, 0, playerMo.momx, playerMo.momy)

	local newx = playerMo.x + P_ReturnThrustX(playerMo,
		travelangle - (player.kartstuff[k_aizdriftstrat] * ANGLE_45), FixedMul(24*FRACUNIT, playerMo.scale))
	local newy = playerMo.y + P_ReturnThrustY(playerMo,
		travelangle - (player.kartstuff[k_aizdriftstrat] * ANGLE_45), FixedMul(24*FRACUNIT, playerMo.scale))
	local dustMobj = P_SpawnMobj(newx, newy, playerMo.z, MT_AIZDRIFTSTRAT)

	dustMobj.angle = travelangle + (player.kartstuff[k_aizdriftstrat] * ANGLE_90)
	dustMobj.destscale = (3 * playerMo.scale) >> 2
	P_SetScale(dustMobj, dustMobj.destscale)

	dustMobj.momx = (6 * playerMo.momx) / 5;
	dustMobj.momy = (6 * playerMo.momy) / 5;
	dustMobj.momz = playerMo.momz / 2;

	K_MatchGenericExtraFlags(dustMobj, playerMo)
	-- print("hello")
end

// i hate porting code i hate porting code i hate
local function K_KartDrift(player)
	local minspeed = 10 * player.mo.scale
	local dsone = K_GetKartDriftSparkValue(player)
	local dstwo = dsone * 2
	local dsthree = dstwo * 2

	-- Grown players taking yellow spring panels will go below minspeed for one tic,
	-- and will then wrongdrift or have their sparks removed because of this.
	-- This fixes this problem.
	if player.kartstuff[k_pogospring] == 2 and player.mo.scale > mapobjectscale then
		minspeed = FixedMul(10 << FRACBITS, mapobjectscale)
	end

	-- Drifting is actually strafing + automatic turning.
	-- Holding the Jump button will enable drifting.

	-- Drift Release (Moved here so you can't "chain" drifts)
	if (player.kartstuff[k_drift] ~= -5 and player.kartstuff[k_drift] ~= 5)
			and player.kartstuff[k_driftcharge] < dsone then
		player.kartstuff[k_driftcharge] = 0
	elseif (player.kartstuff[k_drift] ~= -5 and player.kartstuff[k_drift] ~= 5)
			and (player.kartstuff[k_driftcharge] >= dsone and player.kartstuff[k_driftcharge] < dstwo) then
		if player.kartstuff[k_driftboost] < 20 then
			player.kartstuff[k_driftboost] = 20
		end
		S_StartSound(player.mo, sfx_s23c)
		-- K_SpawnDashDustRelease(player)
		player.kartstuff[k_driftcharge] = 0
	elseif (player.kartstuff[k_drift] ~= -5 and player.kartstuff[k_drift] ~= 5)
			and player.kartstuff[k_driftcharge] < dsthree then
		if player.kartstuff[k_driftboost] < 50 then
			player.kartstuff[k_driftboost] = 50
		end
		S_StartSound(player.mo, sfx_s23c)
		-- K_SpawnDashDustRelease(player)
		player.kartstuff[k_driftcharge] = 0
	elseif (player.kartstuff[k_drift] ~= -5 and player.kartstuff[k_drift] ~= 5)
			and player.kartstuff[k_driftcharge] >= dsthree then
		if player.kartstuff[k_driftboost] < 125 then
			player.kartstuff[k_driftboost] = 125
		end
		S_StartSound(player.mo, sfx_s23c)
		-- K_SpawnDashDustRelease(player)
		player.kartstuff[k_driftcharge] = 0
	end
	
	-- Drifting: left or right?
	if player.cmd.driftturn > 0 and player.wallspeed > minspeed and player.kartstuff[k_jmp] == 1
			and (player.kartstuff[k_drift] == 0 or player.kartstuff[k_driftend] == 1) then
		-- Starting left drift
		player.kartstuff[k_drift] = 1
		player.kartstuff[k_driftend] = 0
	elseif player.cmd.driftturn < 0 and player.wallspeed > minspeed and player.kartstuff[k_jmp] == 1
			and (player.kartstuff[k_drift] == 0 or player.kartstuff[k_driftend] == 1) then
		-- Starting right drift
		player.kartstuff[k_drift] = -1
		player.kartstuff[k_driftend] = 0
	elseif player.kartstuff[k_jmp] == 0 then
		-- drift is not being performed so if we're just finishing set driftend and decrement counters
		if player.kartstuff[k_drift] > 0 then
			player.kartstuff[k_drift] = player.kartstuff[k_drift] - 1
			player.kartstuff[k_driftend] = 1
		elseif player.kartstuff[k_drift] < 0 then
			player.kartstuff[k_drift] = player.kartstuff[k_drift] + 1
			player.kartstuff[k_driftend] = 1
		else
			player.kartstuff[k_driftend] = 0
		end
	end

	-- Increase/decrease the drift value to continue drifting in that direction
	if player.kartstuff[k_spinouttimer] == 0 and player.kartstuff[k_jmp] == 1 and player.kartstuff[k_drift] ~= 0 then
		local driftadditive = 24

		if player.kartstuff[k_drift] >= 1 then -- Drifting to the left
			player.kartstuff[k_drift] = player.kartstuff[k_drift] + 1
			if player.kartstuff[k_drift] > 5 then
				player.kartstuff[k_drift] = 5
			end

			if player.cmd.driftturn > 0 then -- Inward
				driftadditive = driftadditive + abs(player.cmd.driftturn) / 100
			end
			if player.cmd.driftturn < 0 then -- Outward
				driftadditive = driftadditive - abs(player.cmd.driftturn) / 75
			end
		elseif player.kartstuff[k_drift] <= -1 then -- Drifting to the right
			player.kartstuff[k_drift] = player.kartstuff[k_drift] - 1
			if player.kartstuff[k_drift] < -5 then
				player.kartstuff[k_drift] = -5
			end

			if player.cmd.driftturn < 0 then -- Inward
				driftadditive = driftadditive + abs(player.cmd.driftturn) / 100
			end
			if player.cmd.driftturn > 0 then -- Outward
				driftadditive = driftadditive - abs(player.cmd.driftturn) / 75
			end
		end

		-- Disable drift-sparks until you're going fast enough
		if player.kartstuff[k_getsparks] == 0 or (player.kartstuff[k_offroad] and not player.kartstuff[k_invincibilitytimer] and not player.kartstuff[k_hyudorotimer] and not player.kartstuff[k_sneakertimer]) then
			driftadditive = 0
		end
		if player.wallspeed > minspeed * 2 then
			player.kartstuff[k_getsparks] = 1
		end

		-- This spawns the drift sparks
		if player.kartstuff[k_driftcharge] + driftadditive >= dsone then
			K_SpawnDriftSparks(player)
		end

		-- Sound whenever you get a different tier of sparks
		if consoleplayer == player and ((player.kartstuff[k_driftcharge] < dsone and player.kartstuff[k_driftcharge] + driftadditive >= dsone)
				or (player.kartstuff[k_driftcharge] < dstwo and player.kartstuff[k_driftcharge] + driftadditive >= dstwo)
				or (player.kartstuff[k_driftcharge] < dsthree and player.kartstuff[k_driftcharge] + driftadditive >= dsthree)) then
			-- S_StartSound(player.mo, sfx_s3ka2);
			S_StartSoundAtVolume(player.mo, sfx_s3ka2, 192) -- Ugh...
		end

		player.kartstuff[k_driftcharge] = player.kartstuff[k_driftcharge] + driftadditive
		player.kartstuff[k_driftend] = 0
	end

	-- Stop drifting
	if player.kartstuff[k_spinouttimer] > 0 or player.wallspeed < minspeed then
		player.kartstuff[k_driftcharge] = 0
		player.kartstuff[k_drift] = 0
		player.kartstuff[k_aizdriftstrat] = 0
		player.kartstuff[k_aizdriftstrat] = 0
		player.kartstuff[k_getsparks] = 0
	end

	if (not player.kartstuff[k_sneakertimer])
			or (not player.cmd.driftturn)
			or (not player.kartstuff[k_aizdriftstrat])
			or (player.cmd.driftturn > 0) ~= (player.kartstuff[k_aizdriftstrat] > 0) then
		if not player.kartstuff[k_drift] then
			player.kartstuff[k_aizdriftstrat] = 0
		else
			player.kartstuff[k_aizdriftstrat] = (player.kartstuff[k_drift] > 0) and 1 or -1
		end
	else
		if player.kartstuff[k_aizdriftstrat] and not player.kartstuff[k_drift] then
			K_SpawnAIZDust(player)
		end
	end

	if player.kartstuff[k_drift] and ((player.cmd.buttons & BT_BRAKE)
			or not (player.cmd.buttons & BT_ACCELERATE)) then
		if not player.kartstuff[k_brakedrift] then
			K_SpawnBrakeDriftSparks(player)
		end
		player.kartstuff[k_brakedrift] = 1
	else
		player.kartstuff[k_brakedrift] = 0
	end
	
	//print("drift: "..player.kartstuff[k_drift].." charge: "..player.kartstuff[k_driftcharge])
	//print("boost: "..player.kartstuff[k_driftboost])
end

// Turn value lua implementation
rawset(_G, "KL_GetKartTurnValue", function(player, turnvalue)
	local p_maxspeed = FixedMul(K_GetKartSpeed(player, false), 3*FRACUNIT)
	local adjustangle = FixedDiv((p_maxspeed>>16) - (player.speed>>16), (p_maxspeed>>16) + player.kartweight)

	if (player.spectator) then return turnvalue end

	if (player.kartstuff[k_drift] ~= 0)
		// If we're drifting we have a completely different turning value
		
		if (player.kartstuff[k_driftend] == 0)
			// 800 is the max set in g_game.c with angleturn
			local countersteer = turnvalue/800
			turnvalue = (KL_GetKartDriftValue(player, countersteer))
		else
			turnvalue = (turnvalue + KL_GetKartDriftValue(player, 1))
		end

		return turnvalue
	end

	turnvalue = FixedMul(turnvalue, adjustangle) // Weight has a small effect on turning

	if (player.kartstuff[k_invincibilitytimer] or player.kartstuff[k_sneakertimer] or player.kartstuff[k_growshrinktimer] > 0)
		turnvalue = FixedMul(turnvalue, FixedDiv(5*FRACUNIT, 4*FRACUNIT))
	end

	return turnvalue
end)

// courtesy of juggy, hi juggy
local ITEMTIME		= 8 * TICRATE
local function K_CreateRocketSneakers(playerMo)
	K_PlayBoostTaunt(playerMo)
	S_StartSound(playerMo, sfx_s3k3a)

	local player = playerMo.player

	player.kartstuff[k_rocketsneakertimer] = ITEMTIME * 3
	player.kartstuff[k_itemamount] = $ - 1
	
	-- We have to create the rocket sneakers ourselves.
	local prev = playerMo
	for i = 0,1 do
		local rocketSneakerMo = P_SpawnMobj(playerMo.x, playerMo.y, playerMo.z, MT_ROCKETSNEAKER)
		K_MatchGenericExtraFlags(rocketSneakerMo, playerMo)
		rocketSneakerMo.flags		= $ + MF_NOCLIPTHING
		rocketSneakerMo.angle 		= playerMo.angle
		rocketSneakerMo.threshold 	= 10 
		rocketSneakerMo.movecount 	= i % 2
		rocketSneakerMo.movedir		= i + 1
		rocketSneakerMo.lastlook 	= i + 1
		rocketSneakerMo.target 		= playerMo
		rocketSneakerMo.hprev 		= prev
		prev.hnext 					= rocketSneakerMo

		prev 						= rocketSneakerMo
	end
end

local function itemcode(player, playerMo)
	local playerButtons = player.cmd.buttons
	local pks = player.kartstuff

	-- Press item while holding a sneaker to enable the sneaker in mid-air
	-- Holding the button will not use multiple sneakers.
	player.holdingItemBTN = $ or false
	if (playerButtons & BT_ATTACK) == BT_ATTACK and
		player.holdingItemBTN == false then
		
		-- Because you normally cannot use speed items in the air, the
		-- whole behaviour has to be recreated from hardcode to Lua.

		-- Rocket Sneaker behaviour when already deployed.
		if pks[k_rocketsneakertimer] > 1 then
			K_DoSneaker(player, 2) -- taunt
			K_PlayBoostTaunt(playerMo)
			pks[k_rocketsneakertimer] = $ - (2 * TICRATE)
			
			if pks[k_rocketsneakertimer] < 1 then
				pks[k_rocketsneakertimer] = 1
			end
		--Rocket Sneaker behaviour when not deployed.
		elseif pks[k_itemtype] == KITEM_ROCKETSNEAKER then
			K_CreateRocketSneakers(playerMo)
		-- Sneaker behaviour
		elseif pks[k_itemtype] == KITEM_SNEAKER then
			K_DoSneaker(player, 1) -- taunt
			K_PlayBoostTaunt(playerMo)
			pks[k_itemamount] = $ - 1
		end

		-- Make item button holding persist between deaths.
		if player.holdingItemBTN == false then
			player.holdingItemBTN = true
		end
	elseif (playerButtons & BT_ATTACK) ~= BT_ATTACK and
		player.holdingItemBTN == true then -- Let go.
		player.holdingItemBTN = false
	end
end

if not (HalfPipe and HalfPipe.valid) then
	rawset(_G,"HalfPipe",{
	proceed = false, //to know if a map hash alf pie lines
	funcs = {}, //functions goes here
	ver = localver, //identify from other versions to keep it clean
	revision = localrev, //yaay hotfixes exists
	vername = localname //funny
	}) //hello

	HalfPipe.funcs.loadhalfpipelines = LocaStoreHalfPipeLines
	HalfPipe.funcs.clamp = LocalClamp
	HalfPipe.funcs.poitonlineside = localP_PointOnLineSide
	HalfPipe.funcs.pointonline = LocalHalfPipeGetCPOL
	HalfPipe.funcs.gethalfpipeline = LocalNearestHalfPipeLine
	HalfPipe.funcs.sign = LocalSign
	HalfPipe.funcs.angletoint = localAngleToInt
	HalfPipe.funcs.wallfrontangle = LocalWallFrontAngle
	HalfPipe.funcs.accelCode = fuck
	HalfPipe.funcs.ThreedKartMovement = fuck2
	HalfPipe.funcs.getTurnValue = KL_GetKartTurnValue
	HalfPipe.funcs.kansaiDurifto = K_KartDrift
	HalfPipe.funcs.handleItems = itemcode
	
	addHook("MapLoad",HalfPipe.funcs.loadhalfpipelines)
	addHook("NetVars",HalfPipe.funcs.loadhalfpipelines)
	addHook("MapChange",function() halfpipelinessearched = false HalfPipe.proceed = {} end)
end
if (HalfPipe and HalfPipe.valid and HalfPipe.ver < localver ) or (HalfPipe and HalfPipe.valid and HalfPipe.ver == localver and HalfPipe.revision < localrev) then
	HalfPipe.funcs = {}
	HalfPipe.ver = localver
	HalfPipe.revision = localrev
	HalfPipe.vername = localname
	HalfPipe.funcs.loadhalfpipelines = LocaStoreHalfPipeLines
	HalfPipe.funcs.clamp = LocalClamp
	HalfPipe.funcs.poitonlineside = localP_PointOnLineSide
	HalfPipe.funcs.pointonline = LocalHalfPipeGetCPOL
	HalfPipe.funcs.gethalfpipeline = LocalNearestHalfPipeLine
	HalfPipe.funcs.sign = LocalSign
	HalfPipe.funcs.angletoint = localAngleToInt
	HalfPipe.funcs.wallfrontangle = LocalWallFrontAngle
	HalfPipe.funcs.accelCode = fuck
	HalfPipe.funcs.ThreedKartMovement = fuck2
	HalfPipe.funcs.getTurnValue = KL_GetKartTurnValue
	HalfPipe.funcs.kansaiDurifto = K_KartDrift
	HalfPipe.funcs.handleItems = itemcode

end



